#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <common.h>

// 设置内核缓冲区大小
int set_serial_buffer_size(int fd, int buffer_size) {
    struct serial_struct serialOpt;

    // 获取当前串口参数
    if (ioctl(fd, TIOCGSERIAL, &serialOpt) < 0) {
        DEBUG("ioctl TIOCSSERIAL error");
        return -1;
    }

    // 设置输入和输出缓冲区大小
    serialOpt.xmit_fifo_size = buffer_size;
    serialOpt.custom_divisor = 0;

    // 应用配置
    if (ioctl(fd, TIOCSSERIAL, &serialOpt) < 0) {
        DEBUG("ioctl TIOCSSERIAL error");
        return -1;
    }

    return 0;
}

/* serial_set_opt(fd,115200,8,'N',1) */
int serial_set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio, oldtio;

	if (tcgetattr(fd, &oldtio) != 0) {
		DEBUG("SetupSerial 1 error");
		return -1;
	}

	bzero(&newtio, sizeof(newtio));
	newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;

	newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
	newtio.c_oflag &= ~OPOST; /*Output*/

	switch (nBits) {
        case 7:
            newtio.c_cflag |= CS7;
        break;
        case 8:
            newtio.c_cflag |= CS8;
        break;
	}

	switch (nEvent) {
        case 'O':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
        break;
        case 'E':
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
        break;
        case 'N':
            newtio.c_cflag &= ~PARENB;
        break;
	}

	switch (nSpeed) {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
        break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
        break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
        break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
        break;
        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
        break;
	}

	if (nStop == 1)
		newtio.c_cflag &= ~CSTOPB;
	else if (nStop == 2)
		newtio.c_cflag |= CSTOPB;

	newtio.c_cc[VMIN]  = 1; /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
	newtio.c_cc[VTIME] = 1; /* 等待第1个数据的时间: 
	                         * 比如VMIN设为10表示至少读到10个数据才返回,
	                         * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
	                         * 假设VTIME=1，表示: 
	                         *    10秒内一个数据都没有的话就返回
	                         *    如果10秒内至少读到了1个字节，那就继续等待，完全读到VMIN个数据再返回
	                         */

	tcflush(fd, TCIOFLUSH);

	if (tcsetattr(fd, TCSANOW, &newtio) != 0) {
		DEBUG("com set error");
		return -1;
	}

	//DEBUG("set done!\n");
	return 0;
}

int serial_open_port(char *com)
{
	int fd;
	//DEBUG("open serial %s\n", com);
	fd = open(com, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (-1 == fd) {
        DEBUG("serial %s open error", com);
		return -1;
    }
	
    /* 设置串口为非阻塞状态*/
    //if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
    //    perror("serial fcntl");
    //    return -1;
    //}

	DEBUG("open serial %s ok\n", com);
	return fd;
}
